package cn.hill4j.tool.core.obj.collect;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
/**
  * ClassName: TreeTools
  * Description: 树形结构数据构造工具类
  * @author hillchen
  * Date: 2023/8/8
  * @version: 1.0
  */
public class TreeTools {
    private TreeTools(){};
    /**
     * @author hillchen
     * Description 构造树形结构数据
     * Date: 2023/8/8 00:20
     * @param <E> 源对象类型
     * @param <K> 目标分组健值类型
     * @param <T> 目标对象类型
     * @param sources 原数据集合
     * @param converter 数据类型转换器
     * @return List
     **/
    public static <E, K,T extends TreeNode<K,T>> List<T> toTree(Collection<E> sources,  Function<E,T> converter){
        return new ToTreeNodeContext<>(sources,converter).toTree();
    }
    /**
     * @author hillchen
     * Description 构造树形结构数据
     * Date: 2023/8/8 00:21
     * @param <E> 源对象类型
     * @param <K> 目标分组健值类型
     * @param <T> 目标对象类型
     * @param sources 原数据集合
     * @param converter 数据类型转换器
     * @param childrenSetter 树形结构数据子集合设置方法
     * @param keyFun 树形结构数据当前节点健值获取器：从目标类型中提取
     * @param parentKeyFun 树形结构数据父级节点健值获取器：：从目标类型中提取
     * @return List
     **/
    public static <E, K,T> List<T> toTree(Collection<E> sources,  Function<E,T> converter,
                                          BiConsumer<T,List<T>> childrenSetter,
                                          Function<T, K> keyFun, Function<T, K> parentKeyFun){
        return new ToTreeContext<>(sources,converter,childrenSetter,keyFun,parentKeyFun).toTree();
    }
    /**
     * @author hillchen
     * Description 构造树形结构数据
     * Date: 2023/8/8 00:23
     * @param <E> 源对象类型
     * @param <K> 目标分组健值类型
     * @param <T> 目标对象类型
     * @param sources 原数据集合
     * @param keyFun 树形结构数据当前节点健值获取器：从原类型中提取
     * @param parentKeyFun 树形结构数据父级节点健值获取器：从原类型中提取
     * @param converter 数据类型转换器
     * @param childrenSetter 树形结构数据子集合设置方法
     * @return List
     **/
    public static <E, K,T> List<T> toTree(Collection<E> sources,
                                          Function<E, K> keyFun, Function<E, K> parentKeyFun,  Function<E,T> converter,
                                          BiConsumer<T,List<T>> childrenSetter){
        return new ToTreeContext<>(sources,keyFun,parentKeyFun,converter,childrenSetter).toTree();
    }
    /**
     * @author hillchen
     * Description 构造树形结构数据
     * Date: 2023/8/8 00:24
     * @param <E> 源对象类型
     * @param <K> 目标分组健值类型
     * @param <T> 目标对象类型
     * @param sources 原数据集合
     * @param converter 数据类型转换器
     * @param childrenSetter 树形结构数据子集合设置方法
     * @param keyFun 树形结构数据当前节点健值获取器
     * @param parentKeyFun 树形结构数据父级节点健值获取器
     * @return List
     **/
    public static <E, K,T> List<T> toTree(Collection<E> sources,  Function<E,T> converter,
                                          BiConsumer<T,List<T>> childrenSetter,
                                          BiFunction<E,T, K> keyFun, BiFunction<E,T, K> parentKeyFun){
        return new ToTreeContext<>(sources,converter,childrenSetter,keyFun,parentKeyFun).toTree();
    }
    /**
     * @author hillchen
     * Description 构造树形结构数据
     * Date: 2023/8/8 00:24
     * @param <E> 源对象类型
     * @param <K> 目标分组健值类型
     * @param <T> 目标对象类型
     * @param sources 原数据集合
     * @param excluder 原数据元素过滤器
     * @param converter 数据类型转换器
     * @param childrenSetter 树形结构数据子集合设置方法
     * @param tExcluder 目标数据元素过滤器
     * @param keyFun 树形结构数据当前节点健值获取器
     * @param parentKeyFun 树形结构数据父级节点健值获取器
     * @param sort 排序方法
     * @return List
     **/
    public static <E, K,T> List<T> toTree(Collection<E> sources, Predicate<E> excluder, Function<E,T> converter,
                                          BiConsumer<T,List<T>> childrenSetter,Predicate<T> tExcluder,
                                          BiFunction<E,T, K> keyFun, BiFunction<E,T, K> parentKeyFun, Comparator<T> sort){
        return new ToTreeContext<>(sources,converter,childrenSetter,keyFun,parentKeyFun)
                .setExcluder(excluder)
                .setTargetExcluder(tExcluder)
                .setSort(sort)
                .toTree();
    }

    /**
      * ClassName: ToTreeContext
      * Description: 树形结构数据构造上下文
      * @author hillchen
      * @param <E> 源对象类型
      * @param <K> 目标分组健值类型
      * @param <T> 目标对象类型
      * Date: 2023/8/8
      * @version: 1.0
      */
    public static class ToTreeContext<E,K,T> {
        /**
         * 源对象集合
         */
        private  Collection<E> sources;
        /**
         * 原元素过滤器
         */
        private Predicate<E> excluder = CollectionConstant.NO_EXCLUDER;
        /**
         * 目标元素过滤器
         */
        private Predicate<T> targetExcluder = CollectionConstant.NO_EXCLUDER;
        /**
         * 元素转换器
         */
        private Function<E,T> converter;
        /**
         * 树形结构数据子集合设置方法
         */
        private BiConsumer<T,List<T>> childrenSetter;
        /**
         * 树形结构数据当前节点健值获取器
         */
        private BiFunction<E,T, K> keyFun;
        /**
         * 树形结构数据父级节点健值获取器
         */
        private BiFunction<E,T, K> parentKeyFun;
        /**
         * 树形数据结构排序器
         */
        private Comparator<T> sort;

        /**
         * @author hillchen
         * Description  树形结构数据构造上下文构造方法
         * Date: 2023/8/8 00:11
         * @param sources 源对象集合
         * @param converter 元素转换器
         * @param childrenSetter 树形结构数据子集合设置方法
         * @param keyFun 树形结构数据当前节点健值获取器
         * @param parentKeyFun 树形结构数据父级节点健值获取器
         **/
        public ToTreeContext(Collection<E> sources, Function<E, T> converter, BiConsumer<T, List<T>> childrenSetter,
                             BiFunction<E, T, K> keyFun, BiFunction<E, T, K> parentKeyFun) {
            this.sources = sources;
            this.converter = converter;
            this.childrenSetter = childrenSetter;
            this.keyFun = keyFun;
            this.parentKeyFun = parentKeyFun;
        }

        /**
         * @author hillchen
         * Description  树形结构数据构造上下文构造方法
         * Date: 2023/8/8 00:11
         * @param sources 源对象集合
         * @param converter 元素转换器
         * @param childrenSetter 树形结构数据子集合设置方法
         * @param keyFun 树形结构数据当前节点健值获取器:从原对象中提取
         * @param parentKeyFun 树形结构数据父级节点健值获取器:从原对象中提取
         **/
        public ToTreeContext(Collection<E> sources,Function<E, K> keyFun, Function<E, K> parentKeyFun, Function<E, T> converter, BiConsumer<T, List<T>> childrenSetter) {
            this(sources,converter,childrenSetter,(e,t) -> keyFun.apply(e),(e,t) -> parentKeyFun.apply(e));
        }

        /**
         * @author hillchen
         * Description  树形结构数据构造上下文构造方法
         * Date: 2023/8/8 00:11
         * @param sources 源对象集合
         * @param converter 元素转换器
         * @param childrenSetter 树形结构数据子集合设置方法
         * @param keyFun 树形结构数据当前节点健值获取器:从目标对象中提取
         * @param parentKeyFun 树形结构数据父级节点健值获取器:从目标对象中提取
         **/
        public ToTreeContext(Collection<E> sources, Function<E, T> converter, BiConsumer<T, List<T>> childrenSetter,
                             Function<T, K> keyFun, Function<T, K> parentKeyFun) {
            this(sources,converter,childrenSetter,(e,t) -> keyFun.apply(t),(e,t) -> parentKeyFun.apply(t));
        }
        /**
         * @author hillchen
         * Description 设置原元素过滤器
         * Date: 2023/8/8 00:15
         * @param excluder 原元素过滤器
         * @return ToTreeContext
         **/
        public ToTreeContext<E, K,T> setExcluder(Predicate<E> excluder){
            this.excluder = excluder;
            return this;
        }
        /**
         * @author hillchen
         * Description 设置目标元素过滤器
         * Date: 2023/8/8 00:15
         * @param targetExcluder 目标元素过滤器
         * @return ToTreeContext
         **/
        public ToTreeContext<E, K,T> setTargetExcluder(Predicate<T> targetExcluder) {
            this.targetExcluder = targetExcluder;
            return this;
        }

        /**
         * @author hillchen
         * Description 设置排序器
         * Date: 2023/8/8 00:16
         * @param sort 排序器
         * @return ToTreeContext
         **/
        public ToTreeContext<E, K,T>  setSort(Comparator<T> sort) {
            this.sort = sort;
            return this;
        }
        /**
         * @author hillchen
         * Description 执行树形结构数据转换逻辑
         * Date: 2023/8/8 00:17
         * @return List
         **/
        public List<T> toTree() {
            Map<K,List<T>> childrenGroup = new HashMap<>();
            Map<K, Set<K>> childrenKeyGroup = new HashMap<>();
            Map<K,T> nodeMap = new LinkedHashMap<>();
            Set<K> rootSet = new HashSet<>();

            for (E e: sources){
                if (excluder.test(e)){
                    continue;
                }
                T t = converter.apply(e);
                if (targetExcluder.test(t)){
                    continue;
                }

                K key = keyFun.apply(e,t);
                if (nodeMap.containsKey(key)){
                    throw new IllegalStateException("duplicate key fund");
                }
                nodeMap.put(key,t);

                K parentKey = parentKeyFun.apply(e,t);
                List<T> brothers = CollectionTools.getOrDefaultAndPut(childrenGroup,parentKey,() -> new ArrayList<>());
                brothers.add(t);

                if (!nodeMap.containsKey(parentKey)){
                    rootSet.add(key);
                    Set<K> brotherKeys = CollectionTools.getOrDefaultAndPut(childrenKeyGroup,parentKey,() -> new HashSet<>());
                    brotherKeys.add(key);
                }

                if (childrenKeyGroup.containsKey(key)){
                    rootSet.removeAll(childrenKeyGroup.get(key));
                }
            }
            List<T> result = new ArrayList<>(rootSet.size());
            for (Map.Entry<K,T> entry : nodeMap.entrySet()){
                K key = entry.getKey();
                T val = entry.getValue();
                if (childrenGroup.containsKey(key)){
                    List<T> children = childrenGroup.get(key);
                    if (Objects.nonNull(sort)){
                        children.sort(sort);
                    }
                    childrenSetter.accept(val,children);
                }
                if (rootSet.contains(key)){
                    result.add(val);
                }
            }
            if (Objects.nonNull(sort)){
                result.sort(sort);
            }
            return result;
        }
    }
    /**
      * ClassName: ToTreeNodeContext
      * Description: 目标元素实现类树形数据接口的树形结构数据构造上下文
      * @author hillchen
      * Date: 2023/8/8
      * @version: 1.0
      */
    public static class ToTreeNodeContext<E, K,T extends TreeNode<K,T>> extends ToTreeContext<E, K,T>{
        public ToTreeNodeContext(Collection<E> sources, Function<E, T > converter) {
            super(sources,converter,(t,ts) -> t.setChildren(ts),(e,t) -> t.getKey(),(e,t) -> t.getParentKey());
        }
    }
}
